# Check requisite packages are installed.
packages <- c(
  "plotly", 
  "dplyr"
)
for (pkg in packages) {
  library(pkg, character.only = TRUE)
}
package 㤼㸱plotly㤼㸲 was built under R version 4.0.5Loading required package: ggplot2
package 㤼㸱ggplot2㤼㸲 was built under R version 4.0.3
Attaching package: 㤼㸱plotly㤼㸲

The following object is masked from 㤼㸱package:ggplot2㤼㸲:

    last_plot

The following object is masked from 㤼㸱package:stats㤼㸲:

    filter

The following object is masked from 㤼㸱package:graphics㤼㸲:

    layout

package 㤼㸱dplyr㤼㸲 was built under R version 4.0.4
Attaching package: 㤼㸱dplyr㤼㸲

The following objects are masked from 㤼㸱package:stats㤼㸲:

    filter, lag

The following objects are masked from 㤼㸱package:base㤼㸲:

    intersect, setdiff, setequal, union

Load

Pulling code almost directly from LM1996-NumPoolComScaling-Results-2021-05.Rmd.

dirViking <- c(
  file.path(
    getwd(), "LCAB_LawMorton1996-NumericalPoolCommunityScaling"
  ),
  file.path(
    getwd(), "LCAB_LawMorton1996-NumericalPoolCommunityScaling2"
  )
)
dirVikingResults <- file.path(
  dirViking, c("results-2021-04", "save-2021-05-10") # Latter not 100% yet.
)
resultFormat <- paste0(
  "run-", 
  "%d", # Combination Number, or CombnNum.
  "-", 
  "%s", # Run Seed.
  ".RDS"
)

2021-04 Data

# Copied from LawMorton1996-NumericalPoolCommunityScaling-Calculation.R
set.seed(38427042)

basal <- c(3, 10, 30, 100, 300, 1000)
consumer <- c(3, 10, 30, 100, 300, 1000) * 2
events <- (max(basal) + max(consumer)) * 2
runs <- 100

logBodySize <- c(-2, -1, -1, 1) # Morton and Law 1997 version.
parameters <- c(0.01, 10, 0.5, 0.2, 100, 0.1)

# Need to rerun seedsPrep to get the random number generation right for seedsRun
seedsPrep <- runif(2 * length(basal) * length(consumer)) * 1E8
seedsRun <- runif(runs * length(basal) * length(consumer)) * 1E8
paramFrame <- with(list(
  b = rep(basal, times = length(consumer)),
  c = rep(consumer, each = length(basal)),
  s1 = seedsPrep[1:(length(basal) * length(consumer))],
  s2 = seedsPrep[
    (length(basal) * length(consumer) + 1):(
      2 * length(basal) * length(consumer))
  ],
  sR = seedsRun
), {
  temp <- data.frame(
    CombnNum = 0,
    Basals = b,
    Consumers = c,
    SeedPool = s1,
    SeedMat = s2,
    SeedRuns = "",
    SeedRunsNum = 0,
    EndStates = I(rep(list(""), length(b))),
    EndStatesNum = 0,
    EndStateSizes = I(rep(list(""), length(b))),
    EndStateSizesNum = NA,
    EndStateAssembly = I(rep(list(""), length(b))),
    EndStateAbundance = I(rep(list(""), length(b))),
    Dataset = "2021-04",
    DatasetID = 1,
    stringsAsFactors = FALSE
  )
  for (i in 1:nrow(temp)) {
    seeds <- sR[((i - 1) * runs + 1) : (i * runs)]
    temp$SeedRuns[i] <- toString(seeds) # CSV
    temp$SeedRunsNum[i] <- length(seeds)
  }
  temp$CombnNum <- 1:nrow(temp)
  temp
})
# Note: n + 2 end states. Failure to finish, failure to obtain state, and state.
for (i in 1:nrow(paramFrame)) {
  resultsList <- list(
    "No Run" = 0,
    "No State" = 0
  )
  resultsSize <- list(
    "0" = 0
  )
  resultsAssembly <- list(
    "No Run" = data.frame(),
    "No State" = data.frame()
  )
  seeds <- unlist(strsplit(paramFrame$SeedRuns[i], ', '))
  for (seed in seeds) {
    fileName <- file.path(
      dirVikingResults[paramFrame$DatasetID[i]],
      sprintf(resultFormat, paramFrame$CombnNum[i], seed)
    )
    
    if (file.exists(fileName)) {
      temp <- load(fileName)
      temp <- eval(parse(text = temp)) # Get objects.
      
      if (is.data.frame(temp)) {
        community <- toString(
          temp[[ncol(temp)]][[nrow(temp)]]
        )
        size <- toString(length(temp[[ncol(temp)]][[nrow(temp)]]))
        
        if (community == "") {
          resultsList$`No State` <- resultsList$`No State` + 1
          resultsSize$`0` <- resultsSize$`0` + 1
          
        } else if (community %in% names(resultsList)) {
          resultsList[[community]] <- resultsList[[community]] + 1
          resultsSize[[size]] <- resultsSize[[size]] + 1
          
        } else {
          resultsList[[community]] <- 1
          resultsAssembly[[community]] <- temp
          
          if (size %in% resultsSize) {
            resultsSize[[size]] <- resultsSize[[size]] + 1
          } else {
            resultsSize[[size]] <- 1
          }
        }
      } else {
        resultsList$`No State` <- resultsList$`No State` + 1
        resultsSize$`0` <- resultsSize$`0` + 1
      }
    } else {
      resultsList$`No Run` <- resultsList$`No Run` + 1
      resultsSize$`0` <- resultsSize$`0` + 1
    }
  }
  
  paramFrame$EndStates[[i]] <- resultsList
  paramFrame$EndStatesNum[i] <- length(resultsList) - 2 # ! No State, No Run
  paramFrame$EndStateSizes[[i]] <- resultsSize
  paramFrame$EndStateSizesNum[i] <- length(resultsSize) - 1 # ! 0
  paramFrame$EndStateAssembly[[i]] <- resultsAssembly
}

2021-05 Data

source(
  file.path(getwd(), 
            "LawMorton1996-NumericalPoolCommunityScaling-Settings2.R")
)

oldNrow <- nrow(paramFrame)

paramFrame <- rbind(paramFrame, with(list(
  b = rep(basal, times = length(consumer)),
  c = rep(consumer, each = length(basal)),
  s1 = seedsPrep[1:(length(basal) * length(consumer))],
  s2 = seedsPrep[
    (length(basal) * length(consumer) + 1):(
      2 * length(basal) * length(consumer))
  ],
  sR = seedsRun
), {
  temp <- data.frame(
    CombnNum = 0,
    Basals = b,
    Consumers = c,
    SeedPool = s1,
    SeedMat = s2,
    SeedRuns = "",
    SeedRunsNum = 0,
    EndStates = I(rep(list(""), length(b))),
    EndStatesNum = 0,
    EndStateSizes = I(rep(list(""), length(b))),
    EndStateSizesNum = NA,
    EndStateAssembly = I(rep(list(""), length(b))),
    EndStateAbundance = I(rep(list(""), length(b))),
    Dataset = "2021-05",
    DatasetID = 2,
    stringsAsFactors = FALSE
  )
  for (i in 1:nrow(temp)) {
    seeds <- sR[((i - 1) * runs + 1) : (i * runs)]
    temp$SeedRuns[i] <- toString(seeds) # CSV
    temp$SeedRunsNum[i] <- length(seeds)
  }
  temp$CombnNum <- 1:nrow(temp)
  temp
})
)
# Note: n + 2 end states. Failure to finish, failure to obtain state, and state.
# Modified from above, but with the abundance recorded.
for (i in (oldNrow + 1):nrow(paramFrame)) {
  resultsList <- list(
    "No Run" = 0,
    "No State" = 0
  )
  resultsSize <- list(
    "0" = 0
  )
  resultsAssembly <- list(
    "No Run" = data.frame(),
    "No State" = data.frame()
  )
  resultsAbund <- list(
    "No Run" = "",
    "No State" = ""
  )
  seeds <- unlist(strsplit(paramFrame$SeedRuns[i], ', '))
  for (seed in seeds) {
    fileName <- file.path(
      dirVikingResults[paramFrame$DatasetID[i]],
      sprintf(resultFormat, paramFrame$CombnNum[i], seed)
    )
    
    if (file.exists(fileName)) {
      temp <- load(fileName)
      temp <- eval(parse(text = temp)) # Get objects.
      
      if (is.list(temp) && "Result" %in% names(temp)) {
        
        if (is.data.frame(temp$Result))
          community <- temp$Result$Community[[nrow(temp$Result)]]
        else 
          community <- temp$Result
        
        size <- toString(length(community))
        
        if (community[1] != "") 
          abund <- toString(temp$Abund[community + 1])
        else 
          abund <- ""
        
        community <- toString(community)
        
        if (community == "") {
          resultsList$`No State` <- resultsList$`No State` + 1
          resultsSize$`0` <- resultsSize$`0` + 1
          
        } else if (community %in% names(resultsList)) {
          resultsList[[community]] <- resultsList[[community]] + 1
          resultsSize[[size]] <- resultsSize[[size]] + 1
          
        } else {
          resultsList[[community]] <- 1
          resultsAssembly[[community]] <- temp
          resultsAbund[[community]] <- abund
          
          if (size %in% resultsSize) {
            resultsSize[[size]] <- resultsSize[[size]] + 1
          } else {
            resultsSize[[size]] <- 1
          }
        }
      } else {
        resultsList$`No State` <- resultsList$`No State` + 1
        resultsSize$`0` <- resultsSize$`0` + 1
      }
    } else {
      resultsList$`No Run` <- resultsList$`No Run` + 1
      resultsSize$`0` <- resultsSize$`0` + 1
    }
  }
  
  paramFrame$EndStates[[i]] <- resultsList
  paramFrame$EndStatesNum[i] <- length(resultsList) - 2 # ! No State, No Run
  paramFrame$EndStateSizes[[i]] <- resultsSize
  paramFrame$EndStateSizesNum[i] <- length(resultsSize) - 1 # ! 0
  paramFrame$EndStateAssembly[[i]] <- resultsAssembly
  paramFrame$EndStateAbundance[[i]] <- resultsAbund
}

Test Data

testRowNums <- nrow(paramFrame)
testRowsToAdd <- c(2, 6) # Make sure to put in numerical order!

paramFrame <- with(
  list(
    basal2 = c(5, 10, 15),
    consumer2 = c(20, 40, 60),
    logBodySize = c(-2, -1, -1, 0),
    parameters = c(0.01, 10, 0.5, 0.2, 100, 0.1)
  ),
  {
    set.seed(3680180)
    seedsPrep2 <- runif(2 * length(basal2) * length(consumer2)) * 1E8
    with(list(
      b = rep(basal2, times = length(consumer2)),
      c = rep(consumer2, each = length(basal2)),
      s1 = seedsPrep2[1:(length(basal2) * length(consumer2))],
      s2 = seedsPrep2[
        (length(basal2) * length(consumer2) + 1):(
          2 * length(basal2) * length(consumer2))
      ]
    ), {
      rbind(
        paramFrame,
        data.frame(
          CombnNum = testRowsToAdd,
          Basals = b[testRowsToAdd],
          Consumers = c[testRowsToAdd],
          SeedPool = s1[testRowsToAdd],
          SeedMat = s2[testRowsToAdd],
          SeedRuns = "",
          SeedRunsNum = 0,
          EndStates = I(rep(list(""), length(testRowsToAdd))),
          EndStatesNum = 0,
          EndStateSizes = I(rep(list(""), length(testRowsToAdd))),
          EndStateSizesNum = NA,
          EndStateAssembly = I(rep(list(""), length(testRowsToAdd))),
          EndStateAbundance = I(rep(list(""), length(testRowsToAdd))),
          Dataset = "Test",
          DatasetID = max(paramFrame$DatasetID) + 1,
          stringsAsFactors = FALSE
        )
      )
    }
    )
  }
)

testRowNums <- (testRowNums + 1):nrow(paramFrame)
resultsList <- list(
  list(
    "No Run" = 0,
    "No State" = 0,
    "2, 4, 6, 12, 29" = 1,
    "2, 4, 6, 13, 29" = 1
  ),
  list(
    "No Run" = 0,
    "No State" = 0,
    "8, 10, 12, 14, 15, 16, 39, 43" = 1,
    "8, 12, 14, 15, 16, 38, 39" = 1
  )
)
resultsSize <- list(
  list(
    "0" = 0,
    "5" = 2
  ),
  list(
    "0" = 0,
    "8" = 1,
    "7" = 1
  )
)
resultsAbund <- list(
  list(
    "No Run" = "",
    "No State" = "",
    "2, 4, 6, 12, 29" = "742.88553671712, 80.579233072626, 162.128399850253, 20.2082198699389, 18.8589490510429",
    "2, 4, 6, 13, 29" = "668.664143581837, 119.024146851052, 127.680269383867, 30.657960866033, 13.4844194707944"
  ),
  list(
    "No Run" = "",
    "No State" = "",
    "8, 10, 12, 14, 15, 16, 39, 43" = "20.7665807606491, 32.4461165261454, 80.4033387818895, 817.879722033354, 121.136570782828, 18.0390671088957, 12.3834561177271, 19.9674718543196",
    "8, 12, 14, 15, 16, 38, 39" = "82.592048492812, 138.267379166014, 938.158436379166, 51.8610963745021, 5.03556251837491, 14.1019343145825, 25.9231062711228"
  )
)

for (j in seq_along(testRowNums)) {
  i <- testRowNums[j]
  paramFrame$EndStates[[i]] <- resultsList[[j]]
  paramFrame$EndStatesNum[i] <- length(resultsList[[j]]) - 2 # ! No State, No Run
  paramFrame$EndStateSizes[[i]] <- resultsSize[[j]]
  paramFrame$EndStateSizesNum[i] <- length(resultsSize[[j]]) - 1 # ! 0
  paramFrame$EndStateAbundance[[i]] <- resultsAbund[[j]]
}

Plot

# X, Y, Basal and Consumer.
# Z = Sizes of the Endstates.

plotScalingData <- data.frame(
  CombnNum = rep(paramFrame$CombnNum, paramFrame$EndStatesNum),
  Basals = rep(paramFrame$Basals, paramFrame$EndStatesNum),
  Consumers = rep(paramFrame$Consumers, paramFrame$EndStatesNum),
  Dataset = rep(paramFrame$Dataset, paramFrame$EndStatesNum),
  DatasetID = rep(paramFrame$DatasetID, paramFrame$EndStatesNum)
)

# Communities
comms <- unlist(lapply(paramFrame$EndStates, names))
freqs <- unlist(paramFrame$EndStates)
asmbl <- unlist(paramFrame$EndStateAssembly, recursive = FALSE)
asmbl <- asmbl[comms != "No Run" & comms != "No State"]
freqs <- freqs[comms != "No Run" & comms != "No State"]
comms <- comms[comms != "No Run" & comms != "No State"]

asmbl <- lapply(asmbl, function(d) {
  if (is.null(d)) return(NA)
  if ("Result.Outcome" %in% names(d))
    d %>% dplyr::filter(Result.Outcome != "Type 1 (Failure)" & 
                          Result.Outcome != "Present")
  else
    d$Result %>% dplyr::filter(Outcome != "Type 1 (Failure)" & 
                                 Outcome != "Present")
})

plotScalingData$Communities <- comms
plotScalingData$CommunityFreq <- freqs
plotScalingData$CommunitySeq <- asmbl

# Community Size
temp <- unlist(lapply(strsplit(plotScalingData$Communities, ','), length))
plotScalingData$CommunitySize <- temp

# For usage by the reader.

plotScaling <- plotly::plot_ly(
  plotScalingData,
  x = ~Basals,
  y = ~Consumers,
  z = ~CommunitySize,
  color = ~Dataset,
  colors = c("red", "blue", "black")
)

plotScaling <- plotly::add_markers(plotScaling)

plotScaling <- plotly::layout(
  plotScaling,
  scene = list(
    xaxis = list(type = "log"),
    yaxis = list(type = "log"),
    camera = list(
      eye = list(
        x = -1.25, y = -1.25, z = .05
      )
    )
  )
)

plotScaling

Abundances

# > runif(1) * 1E8
# [1] 82598679
set.seed(82598679)

mats <- list()
poolsall <- list() # name pools used in save data; be careful!

for (i in 1:length(dirViking)) {
  temp <- load(file.path(
    dirViking[i], 
    paste0("LawMorton1996-NumericalPoolCommunityScaling-PoolMats", 
           if (i > 1) i else "", 
           ".RDS")
  ))
  mats[[i]] <- eval(parse(text = temp[1]))
  poolsall[[i]] <- eval(parse(text = temp[2]))
}
pools <- poolsall

# Add in the test datasets.
poolsTemp <- list()
matsTemp <- list()
for (r in testRowNums) {
  testRowRow <- paramFrame[r, ]
  poolsTemp[[testRowRow$CombnNum]] <- with(testRowRow,
    RMTRCode2::LawMorton1996_species(
      Basal = Basals,
      Consumer = Consumers,
      Parameters = c(0.01, 10, 0.5, 0.2, 100, 0.1),
      LogBodySize = c(-2, -1, -1, 0),
      seed = SeedPool
    )
  )
  matsTemp[[testRowRow$CombnNum]] <- with(testRowRow,
    RMTRCode2::LawMorton1996_CommunityMat(
      Pool = poolsTemp[[CombnNum]],
      Parameters = c(0.01, 10, 0.5, 0.2, 100, 0.1),
      seed = SeedMat
    )
  )
}
executing %dopar% sequentially: no parallel backend registered
pools[[i + 1]] <- poolsTemp
mats[[i + 1]] <- matsTemp

oldCandidateData <- load(file.path(getwd(), "candidateDataSoFar.Rdata"))
oldCandidateData <- eval(parse(text = oldCandidateData))
candidateData <- plotScalingData %>% dplyr::group_by(
  CombnNum, Dataset
) %>% dplyr::mutate(
  OtherSteadyStates = dplyr::n() - 1
) %>% dplyr::filter(
  OtherSteadyStates > 0
)
candidateData %>% dplyr::select(-CommunitySeq)
# First, check if it is in the paramFrame.
# Second, check if it is in the saved data from the previous.
# Otherwise, ignore it, we'll figure out what it is and why it is missing later.

candidateData$CommunityAbund <- ""

for (r in 1:nrow(candidateData)) {
  # ID 1:4 are used to identify paramFrame, 5 used to identify abundance
  ID <- candidateData[r, 1:6]
  paramFrameRow <- paramFrame %>% dplyr::filter(
    CombnNum == ID$CombnNum,
    Basals == ID$Basals,
    Consumers == ID$Consumers,
    Dataset == ID$Dataset
  )
  
  if (is.list(paramFrameRow$EndStateAbundance[[1]])) {
    entry <- which(ID$Communities == names(paramFrameRow$EndStateAbundance[[1]]))
    if (length(entry)) {
      candidateData$CommunityAbund[r] <- paramFrameRow$EndStateAbundance[[1]][[entry]]
      next()
    }
  }
  
  if (ID$Dataset == "2021-04") {
    
    oldCandDatRow <- oldCandidateData %>% dplyr::filter(
      CombnNum == ID$CombnNum,
      Basals == ID$Basals,
      Consumers == ID$Consumers,
      Communities == ID$Communities
    )
    
    if (nrow(oldCandDatRow) > 0) {
      if (oldCandDatRow$CommunityAbund != "") {
        candidateData$CommunityAbund[r] <- oldCandDatRow$CommunityAbund
      }
    }
  }
}
for (r in 1:nrow(candidateData)) {
  if (!(candidateData$CommunityAbund[r] == "Failure" |
      candidateData$CommunityAbund[r] == "")) next

  # Random guesses, starting from structured.
  temp <- with(
    candidateData[r, ],
    RMTRCode2::FindSteadyStateFromEstimate(
      Pool = pools[[DatasetID]][[CombnNum]],
      InteractionMatrix = mats[[DatasetID]][[CombnNum]],
      Community = Communities,
      Populations = ifelse(
        pools[[DatasetID]][[CombnNum]]$Type[
          RMTRCode2::CsvRowSplit(Communities)
        ] == "Basal",
        1000, 10)
    )
  )

  if (any(temp) < 1E-4) {
    temp <- "EstimateFailure"
  } else {
    temp <- toString(temp)
  }
  candidateData$CommunityAbund[r] <- temp
}
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 2843.06
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 2876.33
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 2870.65
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 2873.23
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 2871.69
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they gocoercing argument of type 'double' to logicalcoercing argument of type 'double' to logicalcoercing argument of type 'double' to logicalcoercing argument of type 'double' to logicalcoercing argument of type 'double' to logicalcoercing argument of type 'double' to logical
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 6597.38
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they gocoercing argument of type 'double' to logical
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3592.76
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3780.53
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3786.89
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3784.07
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3778.43
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3777.75
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3774.98
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they go
DLSODA-  At current T (=R1), MXSTEP (=I1) steps   
      taken on this call before reaching TOUT     
In above message, I1 = 5000
 
In above message, R1 = 3781.84
 
an excessive amount of work (> maxsteps ) was done, but integration was not successful - increase maxstepsReturning early. Results are accurate, as far as they gocoercing argument of type 'double' to logical
candidateData <- candidateData %>% dplyr::filter(CommunityAbund != "",
                                                 CommunityAbund != "Failure",
                                                 CommunityAbund != "EstimateFailure")
candidateData$CommunityProd <- NA
for (r in 1:nrow(candidateData)) {
  candidateData$CommunityProd[r] <- with(
    candidateData[r, ], 
    RMTRCode2::Productivity(
      Pool = pools[[DatasetID]][[CombnNum]], 
      InteractionMatrix = mats[[DatasetID]][[CombnNum]], 
      Community = Communities, 
      Populations = CommunityAbund
    )
  )
}

Simple Island Results

islandFUN <- function(i, dat, pool, mat, dmat) {
  temp <- dat[i, ]
  RMTRCode2::IslandDynamics(
    Pool = pool,
    InteractionMatrix = mat,
    Communities = c(
      list(temp$Communities[1]),
      rep("", nrow(dmat) - 2),
      temp$Communities[2]
    ),
    Populations = c(
      list(temp$CommunityAbund[1]),
      rep("", nrow(dmat) - 2),
      list(temp$CommunityAbund[2])
    ),
    DispersalPool = 0.0001,
    DispersalIsland = dmat,
    Verbose = FALSE
  )
}
# For each group-dataset,
# For each pair,
# Run Island Dynamics,
# Save the result with its pairing
candidateData$TotalID <- paste(candidateData$CombnNum, candidateData$DatasetID)

islandInteractionsOneTwo <- list()

for (grp in unique(candidateData$TotalID)) {
  candidateDataSubset <- candidateData %>% dplyr::filter(TotalID == grp)
  
  if (nrow(candidateDataSubset) == 1) next()
  
  pairingResults <- combn(
    nrow(candidateDataSubset), 2, 
    islandFUN,
    dat = candidateDataSubset, 
    pool = pools[[
      candidateDataSubset$DatasetID[1]
    ]][[candidateDataSubset$CombnNum[1]]],
    mat = mats[[
      candidateDataSubset$DatasetID[1]
    ]][[candidateDataSubset$CombnNum[1]]],
    dmat = matrix(c(0, 1, 1, 0), nrow = 2, ncol = 2),
    simplify = FALSE
  )
  
  pairingResults <- lapply(
    pairingResults, function(mat, isles) {
      mat <- mat[nrow(mat), -1]
      retVal <- list()
      species <- length(mat) / isles
      for (i in 1:isles) {
        retVal[[i]] <- mat[((i - 1) * species + 1) : (i * species)]
      }
      retVal
    },
    isles = 2
  )
  
  islandInteractionsOneTwo[[grp]] <- pairingResults
}
islandInteractionsOneEmptyTwo <- list()

for (grp in unique(candidateData$TotalID)) {
  candidateDataSubset <- candidateData %>% dplyr::filter(TotalID == grp)
  
  if (nrow(candidateDataSubset) == 1) next()
  
  pairingResults <- combn(
    nrow(candidateDataSubset), 2, 
    islandFUN,
    dat = candidateDataSubset, 
    pool = pools[[
      candidateDataSubset$DatasetID[1]
    ]][[candidateDataSubset$CombnNum[1]]],
    mat = mats[[
      candidateDataSubset$DatasetID[1]
    ]][[candidateDataSubset$CombnNum[1]]],
    dmat = matrix(c(
      0, 1, 0, # Island 2 -> 1
      1, 0, 1, # Island 1 -> 2, Island 3 -> 2
      0, 1, 0  # Island 2 -> 3
    ), nrow = 3, ncol = 3, byrow = TRUE),
    simplify = FALSE
  )
  
  pairingResults <- lapply(
    pairingResults, function(mat, isles) {
      mat <- mat[nrow(mat), -1]
      retVal <- list()
      species <- length(mat) / isles
      for (i in 1:isles) {
        retVal[[i]] <- mat[((i - 1) * species + 1) : (i * species)]
      }
      retVal
    },
    isles = 3
  )
  
  islandInteractionsOneEmptyTwo[[grp]] <- pairingResults
}
# Format of table should be:
# ID, Community 1, Community 2, Outcomes 1-2, Outcomes 1-0-2
# For outcomes, species presence will be used.

communities <- NULL
totalCommunities <- NULL
for (grp in unique(candidateData$TotalID)) {
  candidateDataSubset <- candidateData %>% dplyr::filter(TotalID == grp)
  
  if (nrow(candidateDataSubset) > 1) {
    newCommunities <- combn(
      candidateDataSubset$Communities, 2, 
    )
    communities <- c(communities, newCommunities)
    totalCommunities <- c(
      totalCommunities,
      toString(sort(unique(unlist(lapply(newCommunities, 
                                         RMTRCode2::CsvRowSplit)))))
    )
  }
}

islandInteractionsOneTwoWhich <- unlist(lapply(
  seq_along(islandInteractionsOneTwo), function(i, x, tC) {
    lapply(x[[i]], function(y, tC) {
      lapply(y, function(z, tC) {
        toString(RMTRCode2::CsvRowSplit(tC)[which(z > 1E-6)])
      }, tC = tC)
    },
    tC = tC[i])
  },
  x = islandInteractionsOneTwo,
  tC = totalCommunities))

islandInteractionsOneEmptyTwoWhich <- unlist(lapply(
  seq_along(islandInteractionsOneEmptyTwo), function(i, x, tC) {
    lapply(x[[i]], function(y, tC) {
      lapply(y, function(z, tC) {
        toString(RMTRCode2::CsvRowSplit(tC)[which(z > 1E-6)])
      }, tC = tC)
    },
    tC = tC[i])
  },
  x = islandInteractionsOneEmptyTwo,
  tC = totalCommunities))


islandInteractionResults <- data.frame(
  DatasetID = rep(names(islandInteractionsOneTwo), 
                  unlist(lapply(islandInteractionsOneTwo, length))),
  Community1 = communities[seq(from = 1, to = length(communities), by = 2)],
  Community2 = communities[seq(from = 2, to = length(communities), by = 2)],
  OutcomeWOEmpty_Island1 = islandInteractionsOneTwoWhich[
    seq(from = 1, to = length(islandInteractionsOneTwoWhich), by = 2)],
  OutcomeWOEmpty_Island2 = islandInteractionsOneTwoWhich[
    seq(from = 1, to = length(islandInteractionsOneTwoWhich), by = 2)],
  OutcomeWEmpty_Island1 = islandInteractionsOneEmptyTwoWhich[
    seq(from = 1, to = length(islandInteractionsOneEmptyTwoWhich), by = 3)],
  OutcomeWEmpty_Island2 = islandInteractionsOneEmptyTwoWhich[
    seq(from = 1, to = length(islandInteractionsOneEmptyTwoWhich), by = 3)],
  OutcomeWEmpty_Island3 = islandInteractionsOneEmptyTwoWhich[
    seq(from = 1, to = length(islandInteractionsOneEmptyTwoWhich), by = 3)]
)

islandInteractionResults
islandInteractionResults %>% dplyr::mutate(
  C1WOInvaded = Community1 != OutcomeWOEmpty_Island1,
  C2WOInvaded = Community2 != OutcomeWOEmpty_Island2,
  C1WInvaded = Community1 != OutcomeWEmpty_Island1,
  C2WInvaded = Community2 != OutcomeWEmpty_Island3,
  StalemateWO = !C1WOInvaded & !C2WOInvaded,
  StalemateW = !C1WInvaded & !C2WInvaded,
  HybridWO = C1WOInvaded & C2WOInvaded,
  HybridW = C1WInvaded & C2WInvaded,
) %>% dplyr::select(-dplyr::starts_with("Outcome"))

Save workspace

save(
  candidateData,
  islandInteractionsOneEmptyTwo,
  islandInteractionsOneEmptyTwoWhich,
  islandInteractionsOneTwo,
  islandInteractionsOneTwoWhich,
  mats,
  paramFrame,
  plotScalingData,
  pools,
  file = "LM1996-NumPoolCom-QDat-2021-05.RData"
)
LS0tDQp0aXRsZTogIkFuc3dlcmluZyBRdWVzdGlvbnM7IEdhdGhlciBEYXRhLCAyMDIxLTA1Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KLS0tDQoNCmBgYHtyIGxpYnN9DQojIENoZWNrIHJlcXVpc2l0ZSBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkLg0KcGFja2FnZXMgPC0gYygNCiAgInBsb3RseSIsIA0KICAiZHBseXIiDQopDQpmb3IgKHBrZyBpbiBwYWNrYWdlcykgew0KICBsaWJyYXJ5KHBrZywgY2hhcmFjdGVyLm9ubHkgPSBUUlVFKQ0KfQ0KYGBgDQoNCiMgTG9hZA0KUHVsbGluZyBjb2RlIGFsbW9zdCBkaXJlY3RseSBmcm9tIGBMTTE5OTYtTnVtUG9vbENvbVNjYWxpbmctUmVzdWx0cy0yMDIxLTA1LlJtZGAuDQpgYGB7ciBkaXJzfQ0KZGlyVmlraW5nIDwtIGMoDQogIGZpbGUucGF0aCgNCiAgICBnZXR3ZCgpLCAiTENBQl9MYXdNb3J0b24xOTk2LU51bWVyaWNhbFBvb2xDb21tdW5pdHlTY2FsaW5nIg0KICApLA0KICBmaWxlLnBhdGgoDQogICAgZ2V0d2QoKSwgIkxDQUJfTGF3TW9ydG9uMTk5Ni1OdW1lcmljYWxQb29sQ29tbXVuaXR5U2NhbGluZzIiDQogICkNCikNCmRpclZpa2luZ1Jlc3VsdHMgPC0gZmlsZS5wYXRoKA0KICBkaXJWaWtpbmcsIGMoInJlc3VsdHMtMjAyMS0wNCIsICJzYXZlLTIwMjEtMDUtMTAiKSAjIExhdHRlciBub3QgMTAwJSB5ZXQuDQopDQpyZXN1bHRGb3JtYXQgPC0gcGFzdGUwKA0KICAicnVuLSIsIA0KICAiJWQiLCAjIENvbWJpbmF0aW9uIE51bWJlciwgb3IgQ29tYm5OdW0uDQogICItIiwgDQogICIlcyIsICMgUnVuIFNlZWQuDQogICIuUkRTIg0KKQ0KYGBgDQoNCiMjIDIwMjEtMDQgRGF0YQ0KYGBge3IgcGFyYW1zfQ0KIyBDb3BpZWQgZnJvbSBMYXdNb3J0b24xOTk2LU51bWVyaWNhbFBvb2xDb21tdW5pdHlTY2FsaW5nLUNhbGN1bGF0aW9uLlINCnNldC5zZWVkKDM4NDI3MDQyKQ0KDQpiYXNhbCA8LSBjKDMsIDEwLCAzMCwgMTAwLCAzMDAsIDEwMDApDQpjb25zdW1lciA8LSBjKDMsIDEwLCAzMCwgMTAwLCAzMDAsIDEwMDApICogMg0KZXZlbnRzIDwtIChtYXgoYmFzYWwpICsgbWF4KGNvbnN1bWVyKSkgKiAyDQpydW5zIDwtIDEwMA0KDQpsb2dCb2R5U2l6ZSA8LSBjKC0yLCAtMSwgLTEsIDEpICMgTW9ydG9uIGFuZCBMYXcgMTk5NyB2ZXJzaW9uLg0KcGFyYW1ldGVycyA8LSBjKDAuMDEsIDEwLCAwLjUsIDAuMiwgMTAwLCAwLjEpDQoNCiMgTmVlZCB0byByZXJ1biBzZWVkc1ByZXAgdG8gZ2V0IHRoZSByYW5kb20gbnVtYmVyIGdlbmVyYXRpb24gcmlnaHQgZm9yIHNlZWRzUnVuDQpzZWVkc1ByZXAgPC0gcnVuaWYoMiAqIGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpKSAqIDFFOA0Kc2VlZHNSdW4gPC0gcnVuaWYocnVucyAqIGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpKSAqIDFFOA0KYGBgDQoNCmBgYHtyIG9yZ2FuaXNlUGFyYW1zfQ0KcGFyYW1GcmFtZSA8LSB3aXRoKGxpc3QoDQogIGIgPSByZXAoYmFzYWwsIHRpbWVzID0gbGVuZ3RoKGNvbnN1bWVyKSksDQogIGMgPSByZXAoY29uc3VtZXIsIGVhY2ggPSBsZW5ndGgoYmFzYWwpKSwNCiAgczEgPSBzZWVkc1ByZXBbMToobGVuZ3RoKGJhc2FsKSAqIGxlbmd0aChjb25zdW1lcikpXSwNCiAgczIgPSBzZWVkc1ByZXBbDQogICAgKGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpICsgMSk6KA0KICAgICAgMiAqIGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpKQ0KICBdLA0KICBzUiA9IHNlZWRzUnVuDQopLCB7DQogIHRlbXAgPC0gZGF0YS5mcmFtZSgNCiAgICBDb21ibk51bSA9IDAsDQogICAgQmFzYWxzID0gYiwNCiAgICBDb25zdW1lcnMgPSBjLA0KICAgIFNlZWRQb29sID0gczEsDQogICAgU2VlZE1hdCA9IHMyLA0KICAgIFNlZWRSdW5zID0gIiIsDQogICAgU2VlZFJ1bnNOdW0gPSAwLA0KICAgIEVuZFN0YXRlcyA9IEkocmVwKGxpc3QoIiIpLCBsZW5ndGgoYikpKSwNCiAgICBFbmRTdGF0ZXNOdW0gPSAwLA0KICAgIEVuZFN0YXRlU2l6ZXMgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVTaXplc051bSA9IE5BLA0KICAgIEVuZFN0YXRlQXNzZW1ibHkgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVBYnVuZGFuY2UgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRGF0YXNldCA9ICIyMDIxLTA0IiwNCiAgICBEYXRhc2V0SUQgPSAxLA0KICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICApDQogIGZvciAoaSBpbiAxOm5yb3codGVtcCkpIHsNCiAgICBzZWVkcyA8LSBzUlsoKGkgLSAxKSAqIHJ1bnMgKyAxKSA6IChpICogcnVucyldDQogICAgdGVtcCRTZWVkUnVuc1tpXSA8LSB0b1N0cmluZyhzZWVkcykgIyBDU1YNCiAgICB0ZW1wJFNlZWRSdW5zTnVtW2ldIDwtIGxlbmd0aChzZWVkcykNCiAgfQ0KICB0ZW1wJENvbWJuTnVtIDwtIDE6bnJvdyh0ZW1wKQ0KICB0ZW1wDQp9KQ0KYGBgDQoNCmBgYHtyIGxvYWRSZXN1bHRzfQ0KIyBOb3RlOiBuICsgMiBlbmQgc3RhdGVzLiBGYWlsdXJlIHRvIGZpbmlzaCwgZmFpbHVyZSB0byBvYnRhaW4gc3RhdGUsIGFuZCBzdGF0ZS4NCmZvciAoaSBpbiAxOm5yb3cocGFyYW1GcmFtZSkpIHsNCiAgcmVzdWx0c0xpc3QgPC0gbGlzdCgNCiAgICAiTm8gUnVuIiA9IDAsDQogICAgIk5vIFN0YXRlIiA9IDANCiAgKQ0KICByZXN1bHRzU2l6ZSA8LSBsaXN0KA0KICAgICIwIiA9IDANCiAgKQ0KICByZXN1bHRzQXNzZW1ibHkgPC0gbGlzdCgNCiAgICAiTm8gUnVuIiA9IGRhdGEuZnJhbWUoKSwNCiAgICAiTm8gU3RhdGUiID0gZGF0YS5mcmFtZSgpDQogICkNCiAgc2VlZHMgPC0gdW5saXN0KHN0cnNwbGl0KHBhcmFtRnJhbWUkU2VlZFJ1bnNbaV0sICcsICcpKQ0KICBmb3IgKHNlZWQgaW4gc2VlZHMpIHsNCiAgICBmaWxlTmFtZSA8LSBmaWxlLnBhdGgoDQogICAgICBkaXJWaWtpbmdSZXN1bHRzW3BhcmFtRnJhbWUkRGF0YXNldElEW2ldXSwNCiAgICAgIHNwcmludGYocmVzdWx0Rm9ybWF0LCBwYXJhbUZyYW1lJENvbWJuTnVtW2ldLCBzZWVkKQ0KICAgICkNCiAgICANCiAgICBpZiAoZmlsZS5leGlzdHMoZmlsZU5hbWUpKSB7DQogICAgICB0ZW1wIDwtIGxvYWQoZmlsZU5hbWUpDQogICAgICB0ZW1wIDwtIGV2YWwocGFyc2UodGV4dCA9IHRlbXApKSAjIEdldCBvYmplY3RzLg0KICAgICAgDQogICAgICBpZiAoaXMuZGF0YS5mcmFtZSh0ZW1wKSkgew0KICAgICAgICBjb21tdW5pdHkgPC0gdG9TdHJpbmcoDQogICAgICAgICAgdGVtcFtbbmNvbCh0ZW1wKV1dW1tucm93KHRlbXApXV0NCiAgICAgICAgKQ0KICAgICAgICBzaXplIDwtIHRvU3RyaW5nKGxlbmd0aCh0ZW1wW1tuY29sKHRlbXApXV1bW25yb3codGVtcCldXSkpDQogICAgICAgIA0KICAgICAgICBpZiAoY29tbXVuaXR5ID09ICIiKSB7DQogICAgICAgICAgcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCA8LSByZXN1bHRzTGlzdCRgTm8gU3RhdGVgICsgMQ0KICAgICAgICAgIHJlc3VsdHNTaXplJGAwYCA8LSByZXN1bHRzU2l6ZSRgMGAgKyAxDQogICAgICAgICAgDQogICAgICAgIH0gZWxzZSBpZiAoY29tbXVuaXR5ICVpbiUgbmFtZXMocmVzdWx0c0xpc3QpKSB7DQogICAgICAgICAgcmVzdWx0c0xpc3RbW2NvbW11bml0eV1dIDwtIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSArIDENCiAgICAgICAgICByZXN1bHRzU2l6ZVtbc2l6ZV1dIDwtIHJlc3VsdHNTaXplW1tzaXplXV0gKyAxDQogICAgICAgICAgDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgcmVzdWx0c0xpc3RbW2NvbW11bml0eV1dIDwtIDENCiAgICAgICAgICByZXN1bHRzQXNzZW1ibHlbW2NvbW11bml0eV1dIDwtIHRlbXANCiAgICAgICAgICANCiAgICAgICAgICBpZiAoc2l6ZSAlaW4lIHJlc3VsdHNTaXplKSB7DQogICAgICAgICAgICByZXN1bHRzU2l6ZVtbc2l6ZV1dIDwtIHJlc3VsdHNTaXplW1tzaXplXV0gKyAxDQogICAgICAgICAgfSBlbHNlIHsNCiAgICAgICAgICAgIHJlc3VsdHNTaXplW1tzaXplXV0gPC0gMQ0KICAgICAgICAgIH0NCiAgICAgICAgfQ0KICAgICAgfSBlbHNlIHsNCiAgICAgICAgcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCA8LSByZXN1bHRzTGlzdCRgTm8gU3RhdGVgICsgMQ0KICAgICAgICByZXN1bHRzU2l6ZSRgMGAgPC0gcmVzdWx0c1NpemUkYDBgICsgMQ0KICAgICAgfQ0KICAgIH0gZWxzZSB7DQogICAgICByZXN1bHRzTGlzdCRgTm8gUnVuYCA8LSByZXN1bHRzTGlzdCRgTm8gUnVuYCArIDENCiAgICAgIHJlc3VsdHNTaXplJGAwYCA8LSByZXN1bHRzU2l6ZSRgMGAgKyAxDQogICAgfQ0KICB9DQogIA0KICBwYXJhbUZyYW1lJEVuZFN0YXRlc1tbaV1dIDwtIHJlc3VsdHNMaXN0DQogIHBhcmFtRnJhbWUkRW5kU3RhdGVzTnVtW2ldIDwtIGxlbmd0aChyZXN1bHRzTGlzdCkgLSAyICMgISBObyBTdGF0ZSwgTm8gUnVuDQogIHBhcmFtRnJhbWUkRW5kU3RhdGVTaXplc1tbaV1dIDwtIHJlc3VsdHNTaXplDQogIHBhcmFtRnJhbWUkRW5kU3RhdGVTaXplc051bVtpXSA8LSBsZW5ndGgocmVzdWx0c1NpemUpIC0gMSAjICEgMA0KICBwYXJhbUZyYW1lJEVuZFN0YXRlQXNzZW1ibHlbW2ldXSA8LSByZXN1bHRzQXNzZW1ibHkNCn0NCmBgYA0KDQojIyAyMDIxLTA1IERhdGENCmBgYHtyIG9yZ2FuaXNlUGFyYW1zMn0NCnNvdXJjZSgNCiAgZmlsZS5wYXRoKGdldHdkKCksIA0KICAgICAgICAgICAgIkxhd01vcnRvbjE5OTYtTnVtZXJpY2FsUG9vbENvbW11bml0eVNjYWxpbmctU2V0dGluZ3MyLlIiKQ0KKQ0KDQpvbGROcm93IDwtIG5yb3cocGFyYW1GcmFtZSkNCg0KcGFyYW1GcmFtZSA8LSByYmluZChwYXJhbUZyYW1lLCB3aXRoKGxpc3QoDQogIGIgPSByZXAoYmFzYWwsIHRpbWVzID0gbGVuZ3RoKGNvbnN1bWVyKSksDQogIGMgPSByZXAoY29uc3VtZXIsIGVhY2ggPSBsZW5ndGgoYmFzYWwpKSwNCiAgczEgPSBzZWVkc1ByZXBbMToobGVuZ3RoKGJhc2FsKSAqIGxlbmd0aChjb25zdW1lcikpXSwNCiAgczIgPSBzZWVkc1ByZXBbDQogICAgKGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpICsgMSk6KA0KICAgICAgMiAqIGxlbmd0aChiYXNhbCkgKiBsZW5ndGgoY29uc3VtZXIpKQ0KICBdLA0KICBzUiA9IHNlZWRzUnVuDQopLCB7DQogIHRlbXAgPC0gZGF0YS5mcmFtZSgNCiAgICBDb21ibk51bSA9IDAsDQogICAgQmFzYWxzID0gYiwNCiAgICBDb25zdW1lcnMgPSBjLA0KICAgIFNlZWRQb29sID0gczEsDQogICAgU2VlZE1hdCA9IHMyLA0KICAgIFNlZWRSdW5zID0gIiIsDQogICAgU2VlZFJ1bnNOdW0gPSAwLA0KICAgIEVuZFN0YXRlcyA9IEkocmVwKGxpc3QoIiIpLCBsZW5ndGgoYikpKSwNCiAgICBFbmRTdGF0ZXNOdW0gPSAwLA0KICAgIEVuZFN0YXRlU2l6ZXMgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVTaXplc051bSA9IE5BLA0KICAgIEVuZFN0YXRlQXNzZW1ibHkgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRW5kU3RhdGVBYnVuZGFuY2UgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKGIpKSksDQogICAgRGF0YXNldCA9ICIyMDIxLTA1IiwNCiAgICBEYXRhc2V0SUQgPSAyLA0KICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQ0KICApDQogIGZvciAoaSBpbiAxOm5yb3codGVtcCkpIHsNCiAgICBzZWVkcyA8LSBzUlsoKGkgLSAxKSAqIHJ1bnMgKyAxKSA6IChpICogcnVucyldDQogICAgdGVtcCRTZWVkUnVuc1tpXSA8LSB0b1N0cmluZyhzZWVkcykgIyBDU1YNCiAgICB0ZW1wJFNlZWRSdW5zTnVtW2ldIDwtIGxlbmd0aChzZWVkcykNCiAgfQ0KICB0ZW1wJENvbWJuTnVtIDwtIDE6bnJvdyh0ZW1wKQ0KICB0ZW1wDQp9KQ0KKQ0KYGBgDQoNCmBgYHtyIGxvYWRSZXN1bHRzMn0NCiMgTm90ZTogbiArIDIgZW5kIHN0YXRlcy4gRmFpbHVyZSB0byBmaW5pc2gsIGZhaWx1cmUgdG8gb2J0YWluIHN0YXRlLCBhbmQgc3RhdGUuDQojIE1vZGlmaWVkIGZyb20gYWJvdmUsIGJ1dCB3aXRoIHRoZSBhYnVuZGFuY2UgcmVjb3JkZWQuDQpmb3IgKGkgaW4gKG9sZE5yb3cgKyAxKTpucm93KHBhcmFtRnJhbWUpKSB7DQogIHJlc3VsdHNMaXN0IDwtIGxpc3QoDQogICAgIk5vIFJ1biIgPSAwLA0KICAgICJObyBTdGF0ZSIgPSAwDQogICkNCiAgcmVzdWx0c1NpemUgPC0gbGlzdCgNCiAgICAiMCIgPSAwDQogICkNCiAgcmVzdWx0c0Fzc2VtYmx5IDwtIGxpc3QoDQogICAgIk5vIFJ1biIgPSBkYXRhLmZyYW1lKCksDQogICAgIk5vIFN0YXRlIiA9IGRhdGEuZnJhbWUoKQ0KICApDQogIHJlc3VsdHNBYnVuZCA8LSBsaXN0KA0KICAgICJObyBSdW4iID0gIiIsDQogICAgIk5vIFN0YXRlIiA9ICIiDQogICkNCiAgc2VlZHMgPC0gdW5saXN0KHN0cnNwbGl0KHBhcmFtRnJhbWUkU2VlZFJ1bnNbaV0sICcsICcpKQ0KICBmb3IgKHNlZWQgaW4gc2VlZHMpIHsNCiAgICBmaWxlTmFtZSA8LSBmaWxlLnBhdGgoDQogICAgICBkaXJWaWtpbmdSZXN1bHRzW3BhcmFtRnJhbWUkRGF0YXNldElEW2ldXSwNCiAgICAgIHNwcmludGYocmVzdWx0Rm9ybWF0LCBwYXJhbUZyYW1lJENvbWJuTnVtW2ldLCBzZWVkKQ0KICAgICkNCiAgICANCiAgICBpZiAoZmlsZS5leGlzdHMoZmlsZU5hbWUpKSB7DQogICAgICB0ZW1wIDwtIGxvYWQoZmlsZU5hbWUpDQogICAgICB0ZW1wIDwtIGV2YWwocGFyc2UodGV4dCA9IHRlbXApKSAjIEdldCBvYmplY3RzLg0KICAgICAgDQogICAgICBpZiAoaXMubGlzdCh0ZW1wKSAmJiAiUmVzdWx0IiAlaW4lIG5hbWVzKHRlbXApKSB7DQogICAgICAgIA0KICAgICAgICBpZiAoaXMuZGF0YS5mcmFtZSh0ZW1wJFJlc3VsdCkpDQogICAgICAgICAgY29tbXVuaXR5IDwtIHRlbXAkUmVzdWx0JENvbW11bml0eVtbbnJvdyh0ZW1wJFJlc3VsdCldXQ0KICAgICAgICBlbHNlIA0KICAgICAgICAgIGNvbW11bml0eSA8LSB0ZW1wJFJlc3VsdA0KICAgICAgICANCiAgICAgICAgc2l6ZSA8LSB0b1N0cmluZyhsZW5ndGgoY29tbXVuaXR5KSkNCiAgICAgICAgDQogICAgICAgIGlmIChjb21tdW5pdHlbMV0gIT0gIiIpIA0KICAgICAgICAgIGFidW5kIDwtIHRvU3RyaW5nKHRlbXAkQWJ1bmRbY29tbXVuaXR5ICsgMV0pDQogICAgICAgIGVsc2UgDQogICAgICAgICAgYWJ1bmQgPC0gIiINCiAgICAgICAgDQogICAgICAgIGNvbW11bml0eSA8LSB0b1N0cmluZyhjb21tdW5pdHkpDQogICAgICAgIA0KICAgICAgICBpZiAoY29tbXVuaXR5ID09ICIiKSB7DQogICAgICAgICAgcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCA8LSByZXN1bHRzTGlzdCRgTm8gU3RhdGVgICsgMQ0KICAgICAgICAgIHJlc3VsdHNTaXplJGAwYCA8LSByZXN1bHRzU2l6ZSRgMGAgKyAxDQogICAgICAgICAgDQogICAgICAgIH0gZWxzZSBpZiAoY29tbXVuaXR5ICVpbiUgbmFtZXMocmVzdWx0c0xpc3QpKSB7DQogICAgICAgICAgcmVzdWx0c0xpc3RbW2NvbW11bml0eV1dIDwtIHJlc3VsdHNMaXN0W1tjb21tdW5pdHldXSArIDENCiAgICAgICAgICByZXN1bHRzU2l6ZVtbc2l6ZV1dIDwtIHJlc3VsdHNTaXplW1tzaXplXV0gKyAxDQogICAgICAgICAgDQogICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgcmVzdWx0c0xpc3RbW2NvbW11bml0eV1dIDwtIDENCiAgICAgICAgICByZXN1bHRzQXNzZW1ibHlbW2NvbW11bml0eV1dIDwtIHRlbXANCiAgICAgICAgICByZXN1bHRzQWJ1bmRbW2NvbW11bml0eV1dIDwtIGFidW5kDQogICAgICAgICAgDQogICAgICAgICAgaWYgKHNpemUgJWluJSByZXN1bHRzU2l6ZSkgew0KICAgICAgICAgICAgcmVzdWx0c1NpemVbW3NpemVdXSA8LSByZXN1bHRzU2l6ZVtbc2l6ZV1dICsgMQ0KICAgICAgICAgIH0gZWxzZSB7DQogICAgICAgICAgICByZXN1bHRzU2l6ZVtbc2l6ZV1dIDwtIDENCiAgICAgICAgICB9DQogICAgICAgIH0NCiAgICAgIH0gZWxzZSB7DQogICAgICAgIHJlc3VsdHNMaXN0JGBObyBTdGF0ZWAgPC0gcmVzdWx0c0xpc3QkYE5vIFN0YXRlYCArIDENCiAgICAgICAgcmVzdWx0c1NpemUkYDBgIDwtIHJlc3VsdHNTaXplJGAwYCArIDENCiAgICAgIH0NCiAgICB9IGVsc2Ugew0KICAgICAgcmVzdWx0c0xpc3QkYE5vIFJ1bmAgPC0gcmVzdWx0c0xpc3QkYE5vIFJ1bmAgKyAxDQogICAgICByZXN1bHRzU2l6ZSRgMGAgPC0gcmVzdWx0c1NpemUkYDBgICsgMQ0KICAgIH0NCiAgfQ0KICANCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZXNbW2ldXSA8LSByZXN1bHRzTGlzdA0KICBwYXJhbUZyYW1lJEVuZFN0YXRlc051bVtpXSA8LSBsZW5ndGgocmVzdWx0c0xpc3QpIC0gMiAjICEgTm8gU3RhdGUsIE5vIFJ1bg0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNbW2ldXSA8LSByZXN1bHRzU2l6ZQ0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNOdW1baV0gPC0gbGVuZ3RoKHJlc3VsdHNTaXplKSAtIDEgIyAhIDANCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZUFzc2VtYmx5W1tpXV0gPC0gcmVzdWx0c0Fzc2VtYmx5DQogIHBhcmFtRnJhbWUkRW5kU3RhdGVBYnVuZGFuY2VbW2ldXSA8LSByZXN1bHRzQWJ1bmQNCn0NCmBgYA0KDQojIyBUZXN0IERhdGENCg0KYGBge3IgYWRkVGVzdH0NCnRlc3RSb3dOdW1zIDwtIG5yb3cocGFyYW1GcmFtZSkNCnRlc3RSb3dzVG9BZGQgPC0gYygyLCA2KSAjIE1ha2Ugc3VyZSB0byBwdXQgaW4gbnVtZXJpY2FsIG9yZGVyIQ0KDQpwYXJhbUZyYW1lIDwtIHdpdGgoDQogIGxpc3QoDQogICAgYmFzYWwyID0gYyg1LCAxMCwgMTUpLA0KICAgIGNvbnN1bWVyMiA9IGMoMjAsIDQwLCA2MCksDQogICAgbG9nQm9keVNpemUgPSBjKC0yLCAtMSwgLTEsIDApLA0KICAgIHBhcmFtZXRlcnMgPSBjKDAuMDEsIDEwLCAwLjUsIDAuMiwgMTAwLCAwLjEpDQogICksDQogIHsNCiAgICBzZXQuc2VlZCgzNjgwMTgwKQ0KICAgIHNlZWRzUHJlcDIgPC0gcnVuaWYoMiAqIGxlbmd0aChiYXNhbDIpICogbGVuZ3RoKGNvbnN1bWVyMikpICogMUU4DQogICAgd2l0aChsaXN0KA0KICAgICAgYiA9IHJlcChiYXNhbDIsIHRpbWVzID0gbGVuZ3RoKGNvbnN1bWVyMikpLA0KICAgICAgYyA9IHJlcChjb25zdW1lcjIsIGVhY2ggPSBsZW5ndGgoYmFzYWwyKSksDQogICAgICBzMSA9IHNlZWRzUHJlcDJbMToobGVuZ3RoKGJhc2FsMikgKiBsZW5ndGgoY29uc3VtZXIyKSldLA0KICAgICAgczIgPSBzZWVkc1ByZXAyWw0KICAgICAgICAobGVuZ3RoKGJhc2FsMikgKiBsZW5ndGgoY29uc3VtZXIyKSArIDEpOigNCiAgICAgICAgICAyICogbGVuZ3RoKGJhc2FsMikgKiBsZW5ndGgoY29uc3VtZXIyKSkNCiAgICAgIF0NCiAgICApLCB7DQogICAgICByYmluZCgNCiAgICAgICAgcGFyYW1GcmFtZSwNCiAgICAgICAgZGF0YS5mcmFtZSgNCiAgICAgICAgICBDb21ibk51bSA9IHRlc3RSb3dzVG9BZGQsDQogICAgICAgICAgQmFzYWxzID0gYlt0ZXN0Um93c1RvQWRkXSwNCiAgICAgICAgICBDb25zdW1lcnMgPSBjW3Rlc3RSb3dzVG9BZGRdLA0KICAgICAgICAgIFNlZWRQb29sID0gczFbdGVzdFJvd3NUb0FkZF0sDQogICAgICAgICAgU2VlZE1hdCA9IHMyW3Rlc3RSb3dzVG9BZGRdLA0KICAgICAgICAgIFNlZWRSdW5zID0gIiIsDQogICAgICAgICAgU2VlZFJ1bnNOdW0gPSAwLA0KICAgICAgICAgIEVuZFN0YXRlcyA9IEkocmVwKGxpc3QoIiIpLCBsZW5ndGgodGVzdFJvd3NUb0FkZCkpKSwNCiAgICAgICAgICBFbmRTdGF0ZXNOdW0gPSAwLA0KICAgICAgICAgIEVuZFN0YXRlU2l6ZXMgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKHRlc3RSb3dzVG9BZGQpKSksDQogICAgICAgICAgRW5kU3RhdGVTaXplc051bSA9IE5BLA0KICAgICAgICAgIEVuZFN0YXRlQXNzZW1ibHkgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKHRlc3RSb3dzVG9BZGQpKSksDQogICAgICAgICAgRW5kU3RhdGVBYnVuZGFuY2UgPSBJKHJlcChsaXN0KCIiKSwgbGVuZ3RoKHRlc3RSb3dzVG9BZGQpKSksDQogICAgICAgICAgRGF0YXNldCA9ICJUZXN0IiwNCiAgICAgICAgICBEYXRhc2V0SUQgPSBtYXgocGFyYW1GcmFtZSREYXRhc2V0SUQpICsgMSwNCiAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UNCiAgICAgICAgKQ0KICAgICAgKQ0KICAgIH0NCiAgICApDQogIH0NCikNCg0KdGVzdFJvd051bXMgPC0gKHRlc3RSb3dOdW1zICsgMSk6bnJvdyhwYXJhbUZyYW1lKQ0KcmVzdWx0c0xpc3QgPC0gbGlzdCgNCiAgbGlzdCgNCiAgICAiTm8gUnVuIiA9IDAsDQogICAgIk5vIFN0YXRlIiA9IDAsDQogICAgIjIsIDQsIDYsIDEyLCAyOSIgPSAxLA0KICAgICIyLCA0LCA2LCAxMywgMjkiID0gMQ0KICApLA0KICBsaXN0KA0KICAgICJObyBSdW4iID0gMCwNCiAgICAiTm8gU3RhdGUiID0gMCwNCiAgICAiOCwgMTAsIDEyLCAxNCwgMTUsIDE2LCAzOSwgNDMiID0gMSwNCiAgICAiOCwgMTIsIDE0LCAxNSwgMTYsIDM4LCAzOSIgPSAxDQogICkNCikNCnJlc3VsdHNTaXplIDwtIGxpc3QoDQogIGxpc3QoDQogICAgIjAiID0gMCwNCiAgICAiNSIgPSAyDQogICksDQogIGxpc3QoDQogICAgIjAiID0gMCwNCiAgICAiOCIgPSAxLA0KICAgICI3IiA9IDENCiAgKQ0KKQ0KcmVzdWx0c0FidW5kIDwtIGxpc3QoDQogIGxpc3QoDQogICAgIk5vIFJ1biIgPSAiIiwNCiAgICAiTm8gU3RhdGUiID0gIiIsDQogICAgIjIsIDQsIDYsIDEyLCAyOSIgPSAiNzQyLjg4NTUzNjcxNzEyLCA4MC41NzkyMzMwNzI2MjYsIDE2Mi4xMjgzOTk4NTAyNTMsIDIwLjIwODIxOTg2OTkzODksIDE4Ljg1ODk0OTA1MTA0MjkiLA0KICAgICIyLCA0LCA2LCAxMywgMjkiID0gIjY2OC42NjQxNDM1ODE4MzcsIDExOS4wMjQxNDY4NTEwNTIsIDEyNy42ODAyNjkzODM4NjcsIDMwLjY1Nzk2MDg2NjAzMywgMTMuNDg0NDE5NDcwNzk0NCINCiAgKSwNCiAgbGlzdCgNCiAgICAiTm8gUnVuIiA9ICIiLA0KICAgICJObyBTdGF0ZSIgPSAiIiwNCiAgICAiOCwgMTAsIDEyLCAxNCwgMTUsIDE2LCAzOSwgNDMiID0gIjIwLjc2NjU4MDc2MDY0OTEsIDMyLjQ0NjExNjUyNjE0NTQsIDgwLjQwMzMzODc4MTg4OTUsIDgxNy44Nzk3MjIwMzMzNTQsIDEyMS4xMzY1NzA3ODI4MjgsIDE4LjAzOTA2NzEwODg5NTcsIDEyLjM4MzQ1NjExNzcyNzEsIDE5Ljk2NzQ3MTg1NDMxOTYiLA0KICAgICI4LCAxMiwgMTQsIDE1LCAxNiwgMzgsIDM5IiA9ICI4Mi41OTIwNDg0OTI4MTIsIDEzOC4yNjczNzkxNjYwMTQsIDkzOC4xNTg0MzYzNzkxNjYsIDUxLjg2MTA5NjM3NDUwMjEsIDUuMDM1NTYyNTE4Mzc0OTEsIDE0LjEwMTkzNDMxNDU4MjUsIDI1LjkyMzEwNjI3MTEyMjgiDQogICkNCikNCg0KZm9yIChqIGluIHNlcV9hbG9uZyh0ZXN0Um93TnVtcykpIHsNCiAgaSA8LSB0ZXN0Um93TnVtc1tqXQ0KICBwYXJhbUZyYW1lJEVuZFN0YXRlc1tbaV1dIDwtIHJlc3VsdHNMaXN0W1tqXV0NCiAgcGFyYW1GcmFtZSRFbmRTdGF0ZXNOdW1baV0gPC0gbGVuZ3RoKHJlc3VsdHNMaXN0W1tqXV0pIC0gMiAjICEgTm8gU3RhdGUsIE5vIFJ1bg0KICBwYXJhbUZyYW1lJEVuZFN0YXRlU2l6ZXNbW2ldXSA8LSByZXN1bHRzU2l6ZVtbal1dDQogIHBhcmFtRnJhbWUkRW5kU3RhdGVTaXplc051bVtpXSA8LSBsZW5ndGgocmVzdWx0c1NpemVbW2pdXSkgLSAxICMgISAwDQogIHBhcmFtRnJhbWUkRW5kU3RhdGVBYnVuZGFuY2VbW2ldXSA8LSByZXN1bHRzQWJ1bmRbW2pdXQ0KfQ0KDQpgYGANCg0KIyMgUGxvdA0KDQpgYGB7ciBwbG90M0R9DQojIFgsIFksIEJhc2FsIGFuZCBDb25zdW1lci4NCiMgWiA9IFNpemVzIG9mIHRoZSBFbmRzdGF0ZXMuDQoNCnBsb3RTY2FsaW5nRGF0YSA8LSBkYXRhLmZyYW1lKA0KICBDb21ibk51bSA9IHJlcChwYXJhbUZyYW1lJENvbWJuTnVtLCBwYXJhbUZyYW1lJEVuZFN0YXRlc051bSksDQogIEJhc2FscyA9IHJlcChwYXJhbUZyYW1lJEJhc2FscywgcGFyYW1GcmFtZSRFbmRTdGF0ZXNOdW0pLA0KICBDb25zdW1lcnMgPSByZXAocGFyYW1GcmFtZSRDb25zdW1lcnMsIHBhcmFtRnJhbWUkRW5kU3RhdGVzTnVtKSwNCiAgRGF0YXNldCA9IHJlcChwYXJhbUZyYW1lJERhdGFzZXQsIHBhcmFtRnJhbWUkRW5kU3RhdGVzTnVtKSwNCiAgRGF0YXNldElEID0gcmVwKHBhcmFtRnJhbWUkRGF0YXNldElELCBwYXJhbUZyYW1lJEVuZFN0YXRlc051bSkNCikNCg0KIyBDb21tdW5pdGllcw0KY29tbXMgPC0gdW5saXN0KGxhcHBseShwYXJhbUZyYW1lJEVuZFN0YXRlcywgbmFtZXMpKQ0KZnJlcXMgPC0gdW5saXN0KHBhcmFtRnJhbWUkRW5kU3RhdGVzKQ0KYXNtYmwgPC0gdW5saXN0KHBhcmFtRnJhbWUkRW5kU3RhdGVBc3NlbWJseSwgcmVjdXJzaXZlID0gRkFMU0UpDQphc21ibCA8LSBhc21ibFtjb21tcyAhPSAiTm8gUnVuIiAmIGNvbW1zICE9ICJObyBTdGF0ZSJdDQpmcmVxcyA8LSBmcmVxc1tjb21tcyAhPSAiTm8gUnVuIiAmIGNvbW1zICE9ICJObyBTdGF0ZSJdDQpjb21tcyA8LSBjb21tc1tjb21tcyAhPSAiTm8gUnVuIiAmIGNvbW1zICE9ICJObyBTdGF0ZSJdDQoNCmFzbWJsIDwtIGxhcHBseShhc21ibCwgZnVuY3Rpb24oZCkgew0KICBpZiAoaXMubnVsbChkKSkgcmV0dXJuKE5BKQ0KICBpZiAoIlJlc3VsdC5PdXRjb21lIiAlaW4lIG5hbWVzKGQpKQ0KICAgIGQgJT4lIGRwbHlyOjpmaWx0ZXIoUmVzdWx0Lk91dGNvbWUgIT0gIlR5cGUgMSAoRmFpbHVyZSkiICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgIFJlc3VsdC5PdXRjb21lICE9ICJQcmVzZW50IikNCiAgZWxzZQ0KICAgIGQkUmVzdWx0ICU+JSBkcGx5cjo6ZmlsdGVyKE91dGNvbWUgIT0gIlR5cGUgMSAoRmFpbHVyZSkiICYgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBPdXRjb21lICE9ICJQcmVzZW50IikNCn0pDQoNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdGllcyA8LSBjb21tcw0KcGxvdFNjYWxpbmdEYXRhJENvbW11bml0eUZyZXEgPC0gZnJlcXMNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdHlTZXEgPC0gYXNtYmwNCg0KIyBDb21tdW5pdHkgU2l6ZQ0KdGVtcCA8LSB1bmxpc3QobGFwcGx5KHN0cnNwbGl0KHBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdGllcywgJywnKSwgbGVuZ3RoKSkNCnBsb3RTY2FsaW5nRGF0YSRDb21tdW5pdHlTaXplIDwtIHRlbXANCg0KIyBGb3IgdXNhZ2UgYnkgdGhlIHJlYWRlci4NCg0KcGxvdFNjYWxpbmcgPC0gcGxvdGx5OjpwbG90X2x5KA0KICBwbG90U2NhbGluZ0RhdGEsDQogIHggPSB+QmFzYWxzLA0KICB5ID0gfkNvbnN1bWVycywNCiAgeiA9IH5Db21tdW5pdHlTaXplLA0KICBjb2xvciA9IH5EYXRhc2V0LA0KICBjb2xvcnMgPSBjKCJyZWQiLCAiYmx1ZSIsICJibGFjayIpDQopDQoNCnBsb3RTY2FsaW5nIDwtIHBsb3RseTo6YWRkX21hcmtlcnMocGxvdFNjYWxpbmcpDQoNCnBsb3RTY2FsaW5nIDwtIHBsb3RseTo6bGF5b3V0KA0KICBwbG90U2NhbGluZywNCiAgc2NlbmUgPSBsaXN0KA0KICAgIHhheGlzID0gbGlzdCh0eXBlID0gImxvZyIpLA0KICAgIHlheGlzID0gbGlzdCh0eXBlID0gImxvZyIpLA0KICAgIGNhbWVyYSA9IGxpc3QoDQogICAgICBleWUgPSBsaXN0KA0KICAgICAgICB4ID0gLTEuMjUsIHkgPSAtMS4yNSwgeiA9IC4wNQ0KICAgICAgKQ0KICAgICkNCiAgKQ0KKQ0KDQpwbG90U2NhbGluZw0KYGBgDQoNCiMjIEFidW5kYW5jZXMNCg0KYGBge3IgbG9hZFBvb2xzTWF0c30NCiMgPiBydW5pZigxKSAqIDFFOA0KIyBbMV0gODI1OTg2NzkNCnNldC5zZWVkKDgyNTk4Njc5KQ0KDQptYXRzIDwtIGxpc3QoKQ0KcG9vbHNhbGwgPC0gbGlzdCgpICMgbmFtZSBwb29scyB1c2VkIGluIHNhdmUgZGF0YTsgYmUgY2FyZWZ1bCENCg0KZm9yIChpIGluIDE6bGVuZ3RoKGRpclZpa2luZykpIHsNCiAgdGVtcCA8LSBsb2FkKGZpbGUucGF0aCgNCiAgICBkaXJWaWtpbmdbaV0sIA0KICAgIHBhc3RlMCgiTGF3TW9ydG9uMTk5Ni1OdW1lcmljYWxQb29sQ29tbXVuaXR5U2NhbGluZy1Qb29sTWF0cyIsIA0KICAgICAgICAgICBpZiAoaSA+IDEpIGkgZWxzZSAiIiwgDQogICAgICAgICAgICIuUkRTIikNCiAgKSkNCiAgbWF0c1tbaV1dIDwtIGV2YWwocGFyc2UodGV4dCA9IHRlbXBbMV0pKQ0KICBwb29sc2FsbFtbaV1dIDwtIGV2YWwocGFyc2UodGV4dCA9IHRlbXBbMl0pKQ0KfQ0KcG9vbHMgPC0gcG9vbHNhbGwNCg0KIyBBZGQgaW4gdGhlIHRlc3QgZGF0YXNldHMuDQpwb29sc1RlbXAgPC0gbGlzdCgpDQptYXRzVGVtcCA8LSBsaXN0KCkNCmZvciAociBpbiB0ZXN0Um93TnVtcykgew0KICB0ZXN0Um93Um93IDwtIHBhcmFtRnJhbWVbciwgXQ0KICBwb29sc1RlbXBbW3Rlc3RSb3dSb3ckQ29tYm5OdW1dXSA8LSB3aXRoKHRlc3RSb3dSb3csDQogICAgUk1UUkNvZGUyOjpMYXdNb3J0b24xOTk2X3NwZWNpZXMoDQogICAgICBCYXNhbCA9IEJhc2FscywNCiAgICAgIENvbnN1bWVyID0gQ29uc3VtZXJzLA0KICAgICAgUGFyYW1ldGVycyA9IGMoMC4wMSwgMTAsIDAuNSwgMC4yLCAxMDAsIDAuMSksDQogICAgICBMb2dCb2R5U2l6ZSA9IGMoLTIsIC0xLCAtMSwgMCksDQogICAgICBzZWVkID0gU2VlZFBvb2wNCiAgICApDQogICkNCiAgbWF0c1RlbXBbW3Rlc3RSb3dSb3ckQ29tYm5OdW1dXSA8LSB3aXRoKHRlc3RSb3dSb3csDQogICAgUk1UUkNvZGUyOjpMYXdNb3J0b24xOTk2X0NvbW11bml0eU1hdCgNCiAgICAgIFBvb2wgPSBwb29sc1RlbXBbW0NvbWJuTnVtXV0sDQogICAgICBQYXJhbWV0ZXJzID0gYygwLjAxLCAxMCwgMC41LCAwLjIsIDEwMCwgMC4xKSwNCiAgICAgIHNlZWQgPSBTZWVkTWF0DQogICAgKQ0KICApDQp9DQpwb29sc1tbaSArIDFdXSA8LSBwb29sc1RlbXANCm1hdHNbW2kgKyAxXV0gPC0gbWF0c1RlbXANCg0Kb2xkQ2FuZGlkYXRlRGF0YSA8LSBsb2FkKGZpbGUucGF0aChnZXR3ZCgpLCAiY2FuZGlkYXRlRGF0YVNvRmFyLlJkYXRhIikpDQpvbGRDYW5kaWRhdGVEYXRhIDwtIGV2YWwocGFyc2UodGV4dCA9IG9sZENhbmRpZGF0ZURhdGEpKQ0KYGBgDQoNCmBgYHtyIGNvbXB1dGVDYW5kaWRhdGVzfQ0KY2FuZGlkYXRlRGF0YSA8LSBwbG90U2NhbGluZ0RhdGEgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgQ29tYm5OdW0sIERhdGFzZXQNCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIE90aGVyU3RlYWR5U3RhdGVzID0gZHBseXI6Om4oKSAtIDENCikgJT4lIGRwbHlyOjpmaWx0ZXIoDQogIE90aGVyU3RlYWR5U3RhdGVzID4gMA0KKQ0KY2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OnNlbGVjdCgtQ29tbXVuaXR5U2VxKQ0KYGBgDQoNCmBgYHtyIGxvYWRBYnVuZGFuY2VzfQ0KIyBGaXJzdCwgY2hlY2sgaWYgaXQgaXMgaW4gdGhlIHBhcmFtRnJhbWUuDQojIFNlY29uZCwgY2hlY2sgaWYgaXQgaXMgaW4gdGhlIHNhdmVkIGRhdGEgZnJvbSB0aGUgcHJldmlvdXMuDQojIE90aGVyd2lzZSwgaWdub3JlIGl0LCB3ZSdsbCBmaWd1cmUgb3V0IHdoYXQgaXQgaXMgYW5kIHdoeSBpdCBpcyBtaXNzaW5nIGxhdGVyLg0KDQpjYW5kaWRhdGVEYXRhJENvbW11bml0eUFidW5kIDwtICIiDQoNCmZvciAociBpbiAxOm5yb3coY2FuZGlkYXRlRGF0YSkpIHsNCiAgIyBJRCAxOjQgYXJlIHVzZWQgdG8gaWRlbnRpZnkgcGFyYW1GcmFtZSwgNSB1c2VkIHRvIGlkZW50aWZ5IGFidW5kYW5jZQ0KICBJRCA8LSBjYW5kaWRhdGVEYXRhW3IsIDE6Nl0NCiAgcGFyYW1GcmFtZVJvdyA8LSBwYXJhbUZyYW1lICU+JSBkcGx5cjo6ZmlsdGVyKA0KICAgIENvbWJuTnVtID09IElEJENvbWJuTnVtLA0KICAgIEJhc2FscyA9PSBJRCRCYXNhbHMsDQogICAgQ29uc3VtZXJzID09IElEJENvbnN1bWVycywNCiAgICBEYXRhc2V0ID09IElEJERhdGFzZXQNCiAgKQ0KICANCiAgaWYgKGlzLmxpc3QocGFyYW1GcmFtZVJvdyRFbmRTdGF0ZUFidW5kYW5jZVtbMV1dKSkgew0KICAgIGVudHJ5IDwtIHdoaWNoKElEJENvbW11bml0aWVzID09IG5hbWVzKHBhcmFtRnJhbWVSb3ckRW5kU3RhdGVBYnVuZGFuY2VbWzFdXSkpDQogICAgaWYgKGxlbmd0aChlbnRyeSkpIHsNCiAgICAgIGNhbmRpZGF0ZURhdGEkQ29tbXVuaXR5QWJ1bmRbcl0gPC0gcGFyYW1GcmFtZVJvdyRFbmRTdGF0ZUFidW5kYW5jZVtbMV1dW1tlbnRyeV1dDQogICAgICBuZXh0KCkNCiAgICB9DQogIH0NCiAgDQogIGlmIChJRCREYXRhc2V0ID09ICIyMDIxLTA0Iikgew0KICAgIA0KICAgIG9sZENhbmREYXRSb3cgPC0gb2xkQ2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OmZpbHRlcigNCiAgICAgIENvbWJuTnVtID09IElEJENvbWJuTnVtLA0KICAgICAgQmFzYWxzID09IElEJEJhc2FscywNCiAgICAgIENvbnN1bWVycyA9PSBJRCRDb25zdW1lcnMsDQogICAgICBDb21tdW5pdGllcyA9PSBJRCRDb21tdW5pdGllcw0KICAgICkNCiAgICANCiAgICBpZiAobnJvdyhvbGRDYW5kRGF0Um93KSA+IDApIHsNCiAgICAgIGlmIChvbGRDYW5kRGF0Um93JENvbW11bml0eUFidW5kICE9ICIiKSB7DQogICAgICAgIGNhbmRpZGF0ZURhdGEkQ29tbXVuaXR5QWJ1bmRbcl0gPC0gb2xkQ2FuZERhdFJvdyRDb21tdW5pdHlBYnVuZA0KICAgICAgfQ0KICAgIH0NCiAgfQ0KfQ0KYGBgDQoNCmBgYHtyIGNyZWF0ZUFidW5kfQ0KZm9yIChyIGluIDE6bnJvdyhjYW5kaWRhdGVEYXRhKSkgew0KICBpZiAoIShjYW5kaWRhdGVEYXRhJENvbW11bml0eUFidW5kW3JdID09ICJGYWlsdXJlIiB8DQogICAgICBjYW5kaWRhdGVEYXRhJENvbW11bml0eUFidW5kW3JdID09ICIiKSkgbmV4dA0KDQogICMgUmFuZG9tIGd1ZXNzZXMsIHN0YXJ0aW5nIGZyb20gc3RydWN0dXJlZC4NCiAgdGVtcCA8LSB3aXRoKA0KICAgIGNhbmRpZGF0ZURhdGFbciwgXSwNCiAgICBSTVRSQ29kZTI6OkZpbmRTdGVhZHlTdGF0ZUZyb21Fc3RpbWF0ZSgNCiAgICAgIFBvb2wgPSBwb29sc1tbRGF0YXNldElEXV1bW0NvbWJuTnVtXV0sDQogICAgICBJbnRlcmFjdGlvbk1hdHJpeCA9IG1hdHNbW0RhdGFzZXRJRF1dW1tDb21ibk51bV1dLA0KICAgICAgQ29tbXVuaXR5ID0gQ29tbXVuaXRpZXMsDQogICAgICBQb3B1bGF0aW9ucyA9IGlmZWxzZSgNCiAgICAgICAgcG9vbHNbW0RhdGFzZXRJRF1dW1tDb21ibk51bV1dJFR5cGVbDQogICAgICAgICAgUk1UUkNvZGUyOjpDc3ZSb3dTcGxpdChDb21tdW5pdGllcykNCiAgICAgICAgXSA9PSAiQmFzYWwiLA0KICAgICAgICAxMDAwLCAxMCkNCiAgICApDQogICkNCg0KICBpZiAoYW55KHRlbXApIDwgMUUtNCkgew0KICAgIHRlbXAgPC0gIkVzdGltYXRlRmFpbHVyZSINCiAgfSBlbHNlIHsNCiAgICB0ZW1wIDwtIHRvU3RyaW5nKHRlbXApDQogIH0NCiAgY2FuZGlkYXRlRGF0YSRDb21tdW5pdHlBYnVuZFtyXSA8LSB0ZW1wDQp9DQpgYGANCg0KYGBge3IgZmlsdGVyTm9BYnVuZH0NCmNhbmRpZGF0ZURhdGEgPC0gY2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OmZpbHRlcihDb21tdW5pdHlBYnVuZCAhPSAiIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb21tdW5pdHlBYnVuZCAhPSAiRmFpbHVyZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29tbXVuaXR5QWJ1bmQgIT0gIkVzdGltYXRlRmFpbHVyZSIpDQpgYGANCg0KYGBge3IgY29tcHV0ZVByb2R1Y3Rpdml0eX0NCmNhbmRpZGF0ZURhdGEkQ29tbXVuaXR5UHJvZCA8LSBOQQ0KZm9yIChyIGluIDE6bnJvdyhjYW5kaWRhdGVEYXRhKSkgew0KICBjYW5kaWRhdGVEYXRhJENvbW11bml0eVByb2Rbcl0gPC0gd2l0aCgNCiAgICBjYW5kaWRhdGVEYXRhW3IsIF0sIA0KICAgIFJNVFJDb2RlMjo6UHJvZHVjdGl2aXR5KA0KICAgICAgUG9vbCA9IHBvb2xzW1tEYXRhc2V0SURdXVtbQ29tYm5OdW1dXSwgDQogICAgICBJbnRlcmFjdGlvbk1hdHJpeCA9IG1hdHNbW0RhdGFzZXRJRF1dW1tDb21ibk51bV1dLCANCiAgICAgIENvbW11bml0eSA9IENvbW11bml0aWVzLCANCiAgICAgIFBvcHVsYXRpb25zID0gQ29tbXVuaXR5QWJ1bmQNCiAgICApDQogICkNCn0NCmBgYA0KDQojIyBTaW1wbGUgSXNsYW5kIFJlc3VsdHMNCmBgYHtyIGlzbGFuZEZVTn0NCmlzbGFuZEZVTiA8LSBmdW5jdGlvbihpLCBkYXQsIHBvb2wsIG1hdCwgZG1hdCkgew0KICB0ZW1wIDwtIGRhdFtpLCBdDQogIFJNVFJDb2RlMjo6SXNsYW5kRHluYW1pY3MoDQogICAgUG9vbCA9IHBvb2wsDQogICAgSW50ZXJhY3Rpb25NYXRyaXggPSBtYXQsDQogICAgQ29tbXVuaXRpZXMgPSBjKA0KICAgICAgbGlzdCh0ZW1wJENvbW11bml0aWVzWzFdKSwNCiAgICAgIHJlcCgiIiwgbnJvdyhkbWF0KSAtIDIpLA0KICAgICAgdGVtcCRDb21tdW5pdGllc1syXQ0KICAgICksDQogICAgUG9wdWxhdGlvbnMgPSBjKA0KICAgICAgbGlzdCh0ZW1wJENvbW11bml0eUFidW5kWzFdKSwNCiAgICAgIHJlcCgiIiwgbnJvdyhkbWF0KSAtIDIpLA0KICAgICAgbGlzdCh0ZW1wJENvbW11bml0eUFidW5kWzJdKQ0KICAgICksDQogICAgRGlzcGVyc2FsUG9vbCA9IDAuMDAwMSwNCiAgICBEaXNwZXJzYWxJc2xhbmQgPSBkbWF0LA0KICAgIFZlcmJvc2UgPSBGQUxTRQ0KICApDQp9DQpgYGANCg0KYGBge3IgaXNsYW5kT25lVHdvfQ0KIyBGb3IgZWFjaCBncm91cC1kYXRhc2V0LA0KIyBGb3IgZWFjaCBwYWlyLA0KIyBSdW4gSXNsYW5kIER5bmFtaWNzLA0KIyBTYXZlIHRoZSByZXN1bHQgd2l0aCBpdHMgcGFpcmluZw0KY2FuZGlkYXRlRGF0YSRUb3RhbElEIDwtIHBhc3RlKGNhbmRpZGF0ZURhdGEkQ29tYm5OdW0sIGNhbmRpZGF0ZURhdGEkRGF0YXNldElEKQ0KDQppc2xhbmRJbnRlcmFjdGlvbnNPbmVUd28gPC0gbGlzdCgpDQoNCmZvciAoZ3JwIGluIHVuaXF1ZShjYW5kaWRhdGVEYXRhJFRvdGFsSUQpKSB7DQogIGNhbmRpZGF0ZURhdGFTdWJzZXQgPC0gY2FuZGlkYXRlRGF0YSAlPiUgZHBseXI6OmZpbHRlcihUb3RhbElEID09IGdycCkNCiAgDQogIGlmIChucm93KGNhbmRpZGF0ZURhdGFTdWJzZXQpID09IDEpIG5leHQoKQ0KICANCiAgcGFpcmluZ1Jlc3VsdHMgPC0gY29tYm4oDQogICAgbnJvdyhjYW5kaWRhdGVEYXRhU3Vic2V0KSwgMiwgDQogICAgaXNsYW5kRlVOLA0KICAgIGRhdCA9IGNhbmRpZGF0ZURhdGFTdWJzZXQsIA0KICAgIHBvb2wgPSBwb29sc1tbDQogICAgICBjYW5kaWRhdGVEYXRhU3Vic2V0JERhdGFzZXRJRFsxXQ0KICAgIF1dW1tjYW5kaWRhdGVEYXRhU3Vic2V0JENvbWJuTnVtWzFdXV0sDQogICAgbWF0ID0gbWF0c1tbDQogICAgICBjYW5kaWRhdGVEYXRhU3Vic2V0JERhdGFzZXRJRFsxXQ0KICAgIF1dW1tjYW5kaWRhdGVEYXRhU3Vic2V0JENvbWJuTnVtWzFdXV0sDQogICAgZG1hdCA9IG1hdHJpeChjKDAsIDEsIDEsIDApLCBucm93ID0gMiwgbmNvbCA9IDIpLA0KICAgIHNpbXBsaWZ5ID0gRkFMU0UNCiAgKQ0KICANCiAgcGFpcmluZ1Jlc3VsdHMgPC0gbGFwcGx5KA0KICAgIHBhaXJpbmdSZXN1bHRzLCBmdW5jdGlvbihtYXQsIGlzbGVzKSB7DQogICAgICBtYXQgPC0gbWF0W25yb3cobWF0KSwgLTFdDQogICAgICByZXRWYWwgPC0gbGlzdCgpDQogICAgICBzcGVjaWVzIDwtIGxlbmd0aChtYXQpIC8gaXNsZXMNCiAgICAgIGZvciAoaSBpbiAxOmlzbGVzKSB7DQogICAgICAgIHJldFZhbFtbaV1dIDwtIG1hdFsoKGkgLSAxKSAqIHNwZWNpZXMgKyAxKSA6IChpICogc3BlY2llcyldDQogICAgICB9DQogICAgICByZXRWYWwNCiAgICB9LA0KICAgIGlzbGVzID0gMg0KICApDQogIA0KICBpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd29bW2dycF1dIDwtIHBhaXJpbmdSZXN1bHRzDQp9DQpgYGANCg0KYGBge3IgaXNsYW5kT25lRW1wdHlUd299DQppc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3byA8LSBsaXN0KCkNCg0KZm9yIChncnAgaW4gdW5pcXVlKGNhbmRpZGF0ZURhdGEkVG90YWxJRCkpIHsNCiAgY2FuZGlkYXRlRGF0YVN1YnNldCA8LSBjYW5kaWRhdGVEYXRhICU+JSBkcGx5cjo6ZmlsdGVyKFRvdGFsSUQgPT0gZ3JwKQ0KICANCiAgaWYgKG5yb3coY2FuZGlkYXRlRGF0YVN1YnNldCkgPT0gMSkgbmV4dCgpDQogIA0KICBwYWlyaW5nUmVzdWx0cyA8LSBjb21ibigNCiAgICBucm93KGNhbmRpZGF0ZURhdGFTdWJzZXQpLCAyLCANCiAgICBpc2xhbmRGVU4sDQogICAgZGF0ID0gY2FuZGlkYXRlRGF0YVN1YnNldCwgDQogICAgcG9vbCA9IHBvb2xzW1sNCiAgICAgIGNhbmRpZGF0ZURhdGFTdWJzZXQkRGF0YXNldElEWzFdDQogICAgXV1bW2NhbmRpZGF0ZURhdGFTdWJzZXQkQ29tYm5OdW1bMV1dXSwNCiAgICBtYXQgPSBtYXRzW1sNCiAgICAgIGNhbmRpZGF0ZURhdGFTdWJzZXQkRGF0YXNldElEWzFdDQogICAgXV1bW2NhbmRpZGF0ZURhdGFTdWJzZXQkQ29tYm5OdW1bMV1dXSwNCiAgICBkbWF0ID0gbWF0cml4KGMoDQogICAgICAwLCAxLCAwLCAjIElzbGFuZCAyIC0+IDENCiAgICAgIDEsIDAsIDEsICMgSXNsYW5kIDEgLT4gMiwgSXNsYW5kIDMgLT4gMg0KICAgICAgMCwgMSwgMCAgIyBJc2xhbmQgMiAtPiAzDQogICAgKSwgbnJvdyA9IDMsIG5jb2wgPSAzLCBieXJvdyA9IFRSVUUpLA0KICAgIHNpbXBsaWZ5ID0gRkFMU0UNCiAgKQ0KICANCiAgcGFpcmluZ1Jlc3VsdHMgPC0gbGFwcGx5KA0KICAgIHBhaXJpbmdSZXN1bHRzLCBmdW5jdGlvbihtYXQsIGlzbGVzKSB7DQogICAgICBtYXQgPC0gbWF0W25yb3cobWF0KSwgLTFdDQogICAgICByZXRWYWwgPC0gbGlzdCgpDQogICAgICBzcGVjaWVzIDwtIGxlbmd0aChtYXQpIC8gaXNsZXMNCiAgICAgIGZvciAoaSBpbiAxOmlzbGVzKSB7DQogICAgICAgIHJldFZhbFtbaV1dIDwtIG1hdFsoKGkgLSAxKSAqIHNwZWNpZXMgKyAxKSA6IChpICogc3BlY2llcyldDQogICAgICB9DQogICAgICByZXRWYWwNCiAgICB9LA0KICAgIGlzbGVzID0gMw0KICApDQogIA0KICBpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1tbZ3JwXV0gPC0gcGFpcmluZ1Jlc3VsdHMNCn0NCmBgYA0KDQpgYGB7ciBjb21wYXJlSXNsYW5kRHluYW1pY3N9DQojIEZvcm1hdCBvZiB0YWJsZSBzaG91bGQgYmU6DQojIElELCBDb21tdW5pdHkgMSwgQ29tbXVuaXR5IDIsIE91dGNvbWVzIDEtMiwgT3V0Y29tZXMgMS0wLTINCiMgRm9yIG91dGNvbWVzLCBzcGVjaWVzIHByZXNlbmNlIHdpbGwgYmUgdXNlZC4NCg0KY29tbXVuaXRpZXMgPC0gTlVMTA0KdG90YWxDb21tdW5pdGllcyA8LSBOVUxMDQpmb3IgKGdycCBpbiB1bmlxdWUoY2FuZGlkYXRlRGF0YSRUb3RhbElEKSkgew0KICBjYW5kaWRhdGVEYXRhU3Vic2V0IDwtIGNhbmRpZGF0ZURhdGEgJT4lIGRwbHlyOjpmaWx0ZXIoVG90YWxJRCA9PSBncnApDQogIA0KICBpZiAobnJvdyhjYW5kaWRhdGVEYXRhU3Vic2V0KSA+IDEpIHsNCiAgICBuZXdDb21tdW5pdGllcyA8LSBjb21ibigNCiAgICAgIGNhbmRpZGF0ZURhdGFTdWJzZXQkQ29tbXVuaXRpZXMsIDIsIA0KICAgICkNCiAgICBjb21tdW5pdGllcyA8LSBjKGNvbW11bml0aWVzLCBuZXdDb21tdW5pdGllcykNCiAgICB0b3RhbENvbW11bml0aWVzIDwtIGMoDQogICAgICB0b3RhbENvbW11bml0aWVzLA0KICAgICAgdG9TdHJpbmcoc29ydCh1bmlxdWUodW5saXN0KGxhcHBseShuZXdDb21tdW5pdGllcywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJNVFJDb2RlMjo6Q3N2Um93U3BsaXQpKSkpKQ0KICAgICkNCiAgfQ0KfQ0KDQppc2xhbmRJbnRlcmFjdGlvbnNPbmVUd29XaGljaCA8LSB1bmxpc3QobGFwcGx5KA0KICBzZXFfYWxvbmcoaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvKSwgZnVuY3Rpb24oaSwgeCwgdEMpIHsNCiAgICBsYXBwbHkoeFtbaV1dLCBmdW5jdGlvbih5LCB0Qykgew0KICAgICAgbGFwcGx5KHksIGZ1bmN0aW9uKHosIHRDKSB7DQogICAgICAgIHRvU3RyaW5nKFJNVFJDb2RlMjo6Q3N2Um93U3BsaXQodEMpW3doaWNoKHogPiAxRS02KV0pDQogICAgICB9LCB0QyA9IHRDKQ0KICAgIH0sDQogICAgdEMgPSB0Q1tpXSkNCiAgfSwNCiAgeCA9IGlzbGFuZEludGVyYWN0aW9uc09uZVR3bywNCiAgdEMgPSB0b3RhbENvbW11bml0aWVzKSkNCg0KaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd29XaGljaCA8LSB1bmxpc3QobGFwcGx5KA0KICBzZXFfYWxvbmcoaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd28pLCBmdW5jdGlvbihpLCB4LCB0Qykgew0KICAgIGxhcHBseSh4W1tpXV0sIGZ1bmN0aW9uKHksIHRDKSB7DQogICAgICBsYXBwbHkoeSwgZnVuY3Rpb24oeiwgdEMpIHsNCiAgICAgICAgdG9TdHJpbmcoUk1UUkNvZGUyOjpDc3ZSb3dTcGxpdCh0Qylbd2hpY2goeiA+IDFFLTYpXSkNCiAgICAgIH0sIHRDID0gdEMpDQogICAgfSwNCiAgICB0QyA9IHRDW2ldKQ0KICB9LA0KICB4ID0gaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd28sDQogIHRDID0gdG90YWxDb21tdW5pdGllcykpDQoNCg0KaXNsYW5kSW50ZXJhY3Rpb25SZXN1bHRzIDwtIGRhdGEuZnJhbWUoDQogIERhdGFzZXRJRCA9IHJlcChuYW1lcyhpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd28pLCANCiAgICAgICAgICAgICAgICAgIHVubGlzdChsYXBwbHkoaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvLCBsZW5ndGgpKSksDQogIENvbW11bml0eTEgPSBjb21tdW5pdGllc1tzZXEoZnJvbSA9IDEsIHRvID0gbGVuZ3RoKGNvbW11bml0aWVzKSwgYnkgPSAyKV0sDQogIENvbW11bml0eTIgPSBjb21tdW5pdGllc1tzZXEoZnJvbSA9IDIsIHRvID0gbGVuZ3RoKGNvbW11bml0aWVzKSwgYnkgPSAyKV0sDQogIE91dGNvbWVXT0VtcHR5X0lzbGFuZDEgPSBpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd29XaGljaFsNCiAgICBzZXEoZnJvbSA9IDEsIHRvID0gbGVuZ3RoKGlzbGFuZEludGVyYWN0aW9uc09uZVR3b1doaWNoKSwgYnkgPSAyKV0sDQogIE91dGNvbWVXT0VtcHR5X0lzbGFuZDIgPSBpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd29XaGljaFsNCiAgICBzZXEoZnJvbSA9IDEsIHRvID0gbGVuZ3RoKGlzbGFuZEludGVyYWN0aW9uc09uZVR3b1doaWNoKSwgYnkgPSAyKV0sDQogIE91dGNvbWVXRW1wdHlfSXNsYW5kMSA9IGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvV2hpY2hbDQogICAgc2VxKGZyb20gPSAxLCB0byA9IGxlbmd0aChpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1doaWNoKSwgYnkgPSAzKV0sDQogIE91dGNvbWVXRW1wdHlfSXNsYW5kMiA9IGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvV2hpY2hbDQogICAgc2VxKGZyb20gPSAxLCB0byA9IGxlbmd0aChpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1doaWNoKSwgYnkgPSAzKV0sDQogIE91dGNvbWVXRW1wdHlfSXNsYW5kMyA9IGlzbGFuZEludGVyYWN0aW9uc09uZUVtcHR5VHdvV2hpY2hbDQogICAgc2VxKGZyb20gPSAxLCB0byA9IGxlbmd0aChpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3b1doaWNoKSwgYnkgPSAzKV0NCikNCg0KaXNsYW5kSW50ZXJhY3Rpb25SZXN1bHRzDQpgYGANCg0KYGBge3IgbWF0Y2hlc30NCmlzbGFuZEludGVyYWN0aW9uUmVzdWx0cyAlPiUgZHBseXI6Om11dGF0ZSgNCiAgQzFXT0ludmFkZWQgPSBDb21tdW5pdHkxICE9IE91dGNvbWVXT0VtcHR5X0lzbGFuZDEsDQogIEMyV09JbnZhZGVkID0gQ29tbXVuaXR5MiAhPSBPdXRjb21lV09FbXB0eV9Jc2xhbmQyLA0KICBDMVdJbnZhZGVkID0gQ29tbXVuaXR5MSAhPSBPdXRjb21lV0VtcHR5X0lzbGFuZDEsDQogIEMyV0ludmFkZWQgPSBDb21tdW5pdHkyICE9IE91dGNvbWVXRW1wdHlfSXNsYW5kMywNCiAgU3RhbGVtYXRlV08gPSAhQzFXT0ludmFkZWQgJiAhQzJXT0ludmFkZWQsDQogIFN0YWxlbWF0ZVcgPSAhQzFXSW52YWRlZCAmICFDMldJbnZhZGVkLA0KICBIeWJyaWRXTyA9IEMxV09JbnZhZGVkICYgQzJXT0ludmFkZWQsDQogIEh5YnJpZFcgPSBDMVdJbnZhZGVkICYgQzJXSW52YWRlZCwNCikgJT4lIGRwbHlyOjpzZWxlY3QoLWRwbHlyOjpzdGFydHNfd2l0aCgiT3V0Y29tZSIpKQ0KYGBgDQoNCiMgU2F2ZSB3b3Jrc3BhY2UNCmBgYHtyIHNhdmV9DQpzYXZlKA0KICBjYW5kaWRhdGVEYXRhLA0KICBpc2xhbmRJbnRlcmFjdGlvbnNPbmVFbXB0eVR3bywNCiAgaXNsYW5kSW50ZXJhY3Rpb25zT25lRW1wdHlUd29XaGljaCwNCiAgaXNsYW5kSW50ZXJhY3Rpb25zT25lVHdvLA0KICBpc2xhbmRJbnRlcmFjdGlvbnNPbmVUd29XaGljaCwNCiAgbWF0cywNCiAgcGFyYW1GcmFtZSwNCiAgcGxvdFNjYWxpbmdEYXRhLA0KICBwb29scywNCiAgZmlsZSA9ICJMTTE5OTYtTnVtUG9vbENvbS1RRGF0LTIwMjEtMDUuUkRhdGEiDQopDQpgYGANCg==